//==============================================================================
#include "SessionHandler.h"

//==============================================================================
SessionHandler::SessionHandler(ClientSocket* clientSocket, GameServer* server)
{
	setClientSocket(clientSocket);
	setServer(server);
	setIsHandling(true);
	setIsReady(false);
}
//------------------------------------------------------------------------------
SessionHandler::~SessionHandler(void)
{
	delete getClientSocket();
}
//------------------------------------------------------------------------------
void SessionHandler::setSessionType(CLIENT_SESSION_TYPE type)
{
	this->sessionType = type;
}
//------------------------------------------------------------------------------
CLIENT_SESSION_TYPE SessionHandler::getSessionType(void)
{
	return this->sessionType;
}
//------------------------------------------------------------------------------
void SessionHandler::setIsHandling(bool flag)
{
	this->_isHandling = flag;
}
//------------------------------------------------------------------------------
bool SessionHandler::isHandling(void)
{
	return this->_isHandling;
}
//------------------------------------------------------------------------------
void SessionHandler::setIsReady(bool flag)
{
	this->_isReady = flag;
}
//------------------------------------------------------------------------------
bool SessionHandler::isReady(void)
{
	return _isReady;
}
//------------------------------------------------------------------------------
void SessionHandler::setSessionID(int id)
{
	this->sessionID = id;
}
//------------------------------------------------------------------------------
int SessionHandler::getSessionID(void)
{
	return this->sessionID;
}
//------------------------------------------------------------------------------
void SessionHandler::setClientName(string name)
{
	this->clientName = name;
}
//------------------------------------------------------------------------------
string SessionHandler::getClientName(void)
{
	return this->clientName;
}
//------------------------------------------------------------------------------
void SessionHandler::setClientSocket(ClientSocket* socket)
{
	this->clientSocket = socket;
}
//------------------------------------------------------------------------------
ClientSocket* SessionHandler::getClientSocket(void)
{
	return this->clientSocket;
}
//------------------------------------------------------------------------------
void SessionHandler::setServer(GameServer* server)
{
	this->server = server;
}
//------------------------------------------------------------------------------
GameServer* SessionHandler::getServer(void)
{
	return this->server;
}
//------------------------------------------------------------------------------
DWORD SessionHandler::run(void)
{
	return startHandlingSession();
}
//------------------------------------------------------------------------------
void SessionHandler::waitForSceneStateReady()
{
	while(!getServer()->getModel()->isSceneStateReady())

		Sleep(0);
}
//------------------------------------------------------------------------------
int SessionHandler::startHandlingSession(void)
{
	Log::getInstance()->info("[SERVER] Handling session ID: " +  Log::getInstance()->intToString(getSessionID()) + ", started on socket: " + Log::getInstance()->intToString(clientSocket->getSocketConnection()));
	
	ByteBuffer recvbuf;
	try
	{
		while(isHandling())
		{
			getClientSocket()->doReceiveComplete(&recvbuf);
			if (recvbuf.size() > 0)
			{
				Message message(&recvbuf);

				Log::getInstance()->info("[SERVER] Message received : " + message.toString());
		
				processMessage(&message);
							
			}
		}
	}
	catch (NetworkException& ne)
	{
		Log::getInstance()->error("[SERVER] startHandlingSession() ; Conextion error trying to receive client updates..");
		int errorCode = ne.getErrorCode();
		Log::getInstance()->info("[SERVER] Processing desconection session event..");
		setIsReady(false);
		getServer()->deletePlayerSession();
	}	
	return 0;
}
//------------------------------------------------------------------------------
void SessionHandler::stopHandlingSession(void)
{
	setIsHandling(false);
}
//------------------------------------------------------------------------------
void SessionHandler::processMessage(Message* message)
{
	this->waitSceneState = true;
	ByteBuffer sendBuffer;
	switch(message->getType())
	{
		case UNKNOWN_MESSAGE_TYPE:
			{
				Log::getInstance()->info("[SERVER] Processing Message: UNKNOWN_MESSAGE_TYPE");
				//server->updateSceneState(gameEvent);
				Log::getInstance()->error("[SERVER] Unable to process undefined event; this event is discarded.");
			}
			break;
		case NEW_PLAYER_SESSION_REQUEST:
			{
				Log::getInstance()->info("[SERVER] Processing Message: NEW_PLAYER_SESSION_REQUEST");
				if(getServer()->addNewPlayerSession())
				{
					setSessionType(PLAYER_CLIENT_SESSION);
					Message message(NEW_SESSION_REQUEST_ACCEPTED,"");
					message.marshal(&sendBuffer);
					getClientSocket()->doSendComplete(&sendBuffer);
				}
				else
				{
					Message message(NEW_SESSION_REQUEST_REJECTED,"");
					message.marshal(&sendBuffer);
					getClientSocket()->doSendComplete(&sendBuffer);
				}
			}
			break;
		case NEW_OBSERVER_SESSION_REQUEST:
			{
				Log::getInstance()->info("[SERVER] Processing Message: NEW_OBSERVER_SESSION_REQUEST");
				if(getServer()->addNewObserverSession())
				{
					setSessionType(OBSERVER_CLIENT_SESSION);
					Message message(NEW_SESSION_REQUEST_ACCEPTED,"");
					message.marshal(&sendBuffer);
					getClientSocket()->doSendComplete(&sendBuffer);
				}
				else
				{
					Message message(NEW_SESSION_REQUEST_REJECTED,"");
					message.marshal(&sendBuffer);
					getClientSocket()->doSendComplete(&sendBuffer);
				}
			}
			break;
		case SESSION_READY:
			{
				Log::getInstance()->info("[SERVER] Processing Message: SESSION_READY");
				setClientName(message->getData());
				if(getSessionType() == PLAYER_CLIENT_SESSION)
					setSessionID(getServer()->getModel()->addNewTank(getClientName()));
				this->waitSceneState = false;
				setIsReady(true);
				while(!getServer()->allPlayersReady())
				{
					Sleep(0);
				}
				Log::getInstance()->info("[SERVER] Sending START_GAME...");
				Message message(START_GAME,"");
				ByteBuffer sendBuffer;
				message.marshal(&sendBuffer);
				getClientSocket()->doSendComplete(&sendBuffer);
			}
			break;
		case SESSION_END:
			{
				Log::getInstance()->info("[SERVER] Processing Message: SESSION_END");
				GameEvent* gameEvent = new GameEvent(KILL,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				setIsHandling(false);
				this->waitSceneState = false;
			}
			break;
		case GAME_FILES_REQUEST:
			{
				Log::getInstance()->info("[SERVER] Processing Message: GAME_FILES_REQUEST");
				sendGameFiles();
				this->waitSceneState = false;
			}
			break;
		case SCENE_STATE_REQUEST:
			{
				Log::getInstance()->info("[SERVER] Processing Message: SCENE_STATE_REQUEST");
				sendSceneState();
				this->waitSceneState = false;
			}
			break;
		case EVENT_MOVE_UP_ON:
			{
				GameEvent* gameEvent = new GameEvent(MOVE_UP_ON,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_MOVE_UP_OFF:
			{
				GameEvent* gameEvent = new GameEvent(MOVE_UP_OFF,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_MOVE_DOWN_ON:
			{
				GameEvent* gameEvent = new GameEvent(MOVE_DOWN_ON,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_MOVE_DOWN_OFF:
			{
				GameEvent* gameEvent = new GameEvent(MOVE_DOWN_OFF,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_ROT_RIGHT_ON:
			{
				GameEvent* gameEvent = new GameEvent(ROT_RIGHT_ON,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_ROT_RIGHT_OFF:
			{
				GameEvent* gameEvent = new GameEvent(ROT_RIGHT_OFF,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_ROT_LEFT_ON:
			{
				GameEvent* gameEvent = new GameEvent(ROT_LEFT_ON,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_ROT_LEFT_OFF:
			{
				GameEvent* gameEvent = new GameEvent(ROT_LEFT_OFF,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		case EVENT_SHOOT:
			{
				GameEvent* gameEvent = new GameEvent(SHOOT,getSessionID());
				getServer()->addNewGameEvent(gameEvent);
				Log::getInstance()->info("Movement event received.");
			}
			break;
		default:
			{}
	}
}
//------------------------------------------------------------------------------
void SessionHandler::sendGameFiles()
{
	string backgroundPath = getServer()->getModel()->getSceneBackgroundPath();
	Log::getInstance()->info("[SERVER] Sending SceneGame background file..");
	
	ifstream file;
	ifstream::pos_type fileSize = 0;
	char readBuffer[2048];
	char sendBuffer[1024];

	char* startFileCmd = "STARTFILE";
	char* endFileCmd = "ENDFILE";

	ifstream::pos_type readSize = 2048;
	int sendSize = 1024;
	ifstream::pos_type bytesInReadBuffer = 0;
	ifstream::pos_type zeroBytes = 0;
	int bytesInSendBuffer = 0;
	ifstream::pos_type totalBytesRead = 0;

	size_t index = backgroundPath.find_last_of("\\");
	string filename = backgroundPath.substr(index+1);

	file.open(backgroundPath, ios::in | ios::binary | ios::ate);
	if (file.is_open())
	{
		send(getClientSocket()->getSocketConnection(), startFileCmd, (int)strlen(startFileCmd), 0);
		cout << "Sent STARTFILE command." << endl;
		Sleep(100);
		send(getClientSocket()->getSocketConnection(), filename.c_str(), (int)strlen(filename.c_str()), 0);
		cout << "Sent background file name." << endl;
		Sleep(100);
		fileSize = file.tellg();
		cout << "File size: " << fileSize << endl;
		file.seekg(0, ios::beg);
		int bytesSent = 0;
		int lastBytesSent = 0;
		int totalBytesSent = 0;
		while (file.good() && totalBytesRead < fileSize)
		{
			if (bytesInReadBuffer <	1024 && totalBytesRead < fileSize)
			{
				if (bytesInReadBuffer != zeroBytes)
				{
					memcpy(readBuffer, readBuffer+(readSize-bytesInReadBuffer), bytesInReadBuffer);
				}

				if (fileSize-totalBytesRead < readSize-bytesInReadBuffer)
				{
					file.read(readBuffer+bytesInReadBuffer, fileSize-totalBytesRead);
					bytesInReadBuffer += (fileSize-totalBytesRead);
					totalBytesRead = fileSize;
				}
				else
				{
					file.read(readBuffer+bytesInReadBuffer, readSize-bytesInReadBuffer);
					totalBytesRead += (readSize-bytesInReadBuffer);
					bytesInReadBuffer = readSize;
				}
			}

			if (bytesInSendBuffer < 512 && bytesInReadBuffer > 0)
			{
				if (bytesInSendBuffer != zeroBytes)
				{
					memcpy(sendBuffer, sendBuffer+(sendSize-bytesInSendBuffer), bytesInSendBuffer);
				}

				if (bytesInReadBuffer < sendSize-bytesInSendBuffer)
				{
					memcpy(sendBuffer+bytesInSendBuffer, readBuffer, bytesInReadBuffer);//+(readSize-bytesInReadBuffer)
					bytesInSendBuffer += bytesInReadBuffer;
					bytesInReadBuffer = 0;
				}
				else
				{
					memcpy(sendBuffer+bytesInSendBuffer, readBuffer+(readSize-bytesInReadBuffer), sendSize-bytesInSendBuffer);
					bytesInReadBuffer -= (sendSize-bytesInSendBuffer);
					bytesInSendBuffer = sendSize;
				}
				bytesSent = 0;
			}

			if (bytesInSendBuffer > 0)
			{
				lastBytesSent = send(getClientSocket()->getSocketConnection(), sendBuffer+bytesSent, bytesInSendBuffer, 0);//sendSize-bytesSent, 0);
				if (lastBytesSent == SOCKET_ERROR)
				{
					cout << "send failed with error: " << WSAGetLastError() << endl;
				}
				else
				{
					cout << "Sent " << lastBytesSent << " bytes." << endl;
				}
			}
			bytesInSendBuffer -= lastBytesSent;
			bytesSent += lastBytesSent;
			totalBytesSent += lastBytesSent;
		}
		file.close();
		cout << "Total bytes read from file: " << totalBytesRead << endl;
		cout << "Total bytes sent: " << totalBytesSent << endl;
		Sleep(200);
		send(getClientSocket()->getSocketConnection(), endFileCmd, (int)strlen(endFileCmd), 0);
		cout << "Sent ENDFILE command" << endl;
		Sleep(200);
	}
	else
	{
		cout << "Unable to open file." << endl;
	}
}
//------------------------------------------------------------------------------
void SessionHandler::sendSceneState(void)
{
	char sendBuffer[1024];
	int totalBytesRead=0;
	int bytesInReadBuffer=0;
	int bytesToSend;
	int serialSize=0;
	int remainingBytes;
	int totalBytesSent=0;
	char* startFileCmd = "STARTSCENE";
	char* endFileCmd = "ENDSCENE";
	ByteBuffer* b;
	b=this->server->getModel()->getSceneState();
	serialSize = b->size();
	remainingBytes = serialSize;
	const char* readBuffer = b->get();
	Log::getInstance()->info("Sending scene state.");
	stringstream ss;
	ss << serialSize;	
	send(getClientSocket()->getSocketConnection(), ss.str().c_str(), (int)strlen(ss.str().c_str()), 0);
	cout << "Sent Scene size:" << ss.str()<< endl;
	cout << "Sent Scene size with lenght" << (int)strlen(ss.str().c_str())<< endl;
	Sleep(100);

	while (totalBytesSent < serialSize)
		{
			//if (bytesInReadBuffer <	1024 && totalBytesRead < serialSize)
			if(serialSize < 1024 )
			{
				int remoteSendResult = send(getClientSocket()->getSocketConnection(), b->get(), b->size(), 0);
				if (remoteSendResult == SOCKET_ERROR)
				{
					cout << "Error forwarding message." << endl;
				}else{
					cout << "Sent:"<< remoteSendResult <<" bytes." << endl;
				}
				totalBytesSent += remoteSendResult;
				Sleep(200);
			}else{
				if(remainingBytes > 1024){
					bytesToSend=1024;
				}else{
					bytesToSend=remainingBytes;
				}
				memcpy(sendBuffer, readBuffer+(totalBytesSent), bytesToSend);
				int remoteSendResult = send(getClientSocket()->getSocketConnection(), sendBuffer, bytesToSend, 0);
				if (remoteSendResult == SOCKET_ERROR)
				{
					cout << "Error forwarding message." << endl;
				}else{
					cout << "Sent:"<< remoteSendResult <<" bytes." << endl;
				}
				totalBytesSent += remoteSendResult;
				remainingBytes -= bytesToSend;
				Sleep(200);

			}
			
		//	if (bytesInSendBuffer > 0)
		//	{
		//		lastBytesSent = send(socket, sendBuffer+bytesSent, bytesInSendBuffer, 0);//sendSize-bytesSent, 0);
		//		if (lastBytesSent == SOCKET_ERROR)
		//		{
		//			cout << "send failed with error: " << WSAGetLastError() << endl;
		//		}
		//		else
		//		{
		//			cout << "Sent " << lastBytesSent << " bytes." << endl;
		//		}
		//	}
		//	bytesInSendBuffer -= lastBytesSent;
		//	bytesSent += lastBytesSent;
		//	totalBytesSent += lastBytesSent;
		//}
	}
	Sleep(200);
	char recvbuf[1024];
    int recvbuflen = 1024;
	int receiveResult;
	char* pBuf;
	bool sceneReceived = false;
	string bytes = "";
	string command = "";
	string startFileCommand = "SCENERECIBIDO";
	string endFileCommand = "ENDSCENE";
	cout << "Sent Scene of "<<totalBytesSent<<" bytes." << endl;
	
	recv(getClientSocket()->getSocketConnection(), recvbuf, recvbuflen, 0);
	command = bytes.append(recvbuf).substr(0, startFileCommand.length());

	if (command.compare(startFileCommand) == 0){
		sceneReceived = true;
		cout << "Received SCENERECEIVED confirmation." <<  endl;	
	}

	/*send(socket, endFileCmd, (int)strlen(endFileCmd), 0);
		cout << "Sent ENDSCENE command" << endl;*/
}
